#include "fdt_context.h"
#include <libfdt/libfdt.h>
#include <boot/boot.h>      /* boot_get_dtb() 或者 limine wrapper */
#include <mm/mm.h>          /* phys_to_virt() */

fdt_context_t g_fdt_ctx = {0};

__attribute__((
    used,
    section(".limine_requests"))) static volatile struct limine_dtb_request
    dtb_request = {
        .id = LIMINE_DTB_REQUEST,
        .revision = 0,
        .response = NULL,
};

uint64_t boot_get_dtb() {
    return dtb_request.response ? (uint64_t)dtb_request.response->dtb_ptr : 0;
}

int fdt_context_init(uint64_t dtb_addr)
{
    if (!dtb_addr) {
        printk("[FDT] invalid DTB addr=0x%lx\n", dtb_addr);
        return -1;
    }

    /* 自动判断地址类型 */
    const void *fdt = NULL;
    // if (is_kernel_virt_addr(dtb_addr)) {
    fdt = (const void *)(uintptr_t)dtb_addr;
    // printk("[FDT] Using DTB as virtual address: 0x%lx\n", dtb_addr);
    // } else {
        // fdt = (const void *)(uintptr_t)phys_to_virt(dtb_addr);
        // printk("[FDT] Converted phys->virt: 0x%lx -> %p\n", dtb_addr, fdt);
    // }

    if (!fdt) {
        printk("[FDT] failed: NULL pointer after translation\n");
        return -2;
    }

    int err = fdt_check_header(fdt);
    if (err) {
        printk("[FDT] Invalid header: %s\n", fdt_strerror(err));
        return -3;
    }

    /* 保存到全局上下文 */
    g_fdt_ctx.fdt         = fdt;
    g_fdt_ctx.totalsize   = fdt_totalsize(fdt);
    g_fdt_ctx.struct_blk  = (const void *)((const char *)fdt + fdt_off_dt_struct(fdt));
    g_fdt_ctx.strings_blk = (const char *)fdt + fdt_off_dt_strings(fdt);
    g_fdt_ctx.reserve_map = (const void *)((const char *)fdt + fdt_off_mem_rsvmap(fdt));
    g_fdt_ctx.initialized = 1;

    // printk("[FDT] Initialized, total size = %u bytes\n", g_fdt_ctx.totalsize);
    return 0;
}


int fdt_context_init_from_boot(void)
{
    uint64_t dtb = boot_get_dtb();
    // 打印格式更直观（十六进制+十进制）
    // printk("[FDT] boot_get_dtb() = 0x%lx (%lu)\n", dtb, dtb);
    return fdt_context_init(dtb);
}

int fdt_ctx_find_node(const char *path)
{
    if (!g_fdt_ctx.initialized) return -1;
    return fdt_path_offset(g_fdt_ctx.fdt, path);
}

const void *fdt_ctx_get_prop(int node, const char *prop, int *len)
{
    if (!g_fdt_ctx.initialized) return NULL;
    return fdt_getprop(g_fdt_ctx.fdt, node, prop, len);
}

int fdt_ctx_get_u32(int node, const char *prop, uint32_t *out)
{
    int len = 0;
    const void *p = fdt_ctx_get_prop(node, prop, &len);
    if (!p || len < (int)sizeof(uint32_t)) return -1;
    uint32_t raw = *(const uint32_t *)p;
    *out = fdt32_to_cpu(raw);
    return 0;
}

int fdt_ctx_get_u64(int node, const char *prop, uint64_t *out)
{
    int len = 0;
    const void *p = fdt_ctx_get_prop(node, prop, &len);
    if (!p || len < (int)sizeof(uint64_t)) return -1;
    uint64_t raw = *(const uint64_t *)p;
    *out = fdt64_to_cpu(raw);
    return 0;
}

const char *fdt_ctx_get_string(int node, const char *prop)
{
    int len = 0;
    const char *s = (const char *)fdt_ctx_get_prop(node, prop, &len);
    if (!s) return NULL;
    if (len <= 0 || s[len-1] != '\0') return NULL;
    return s;
}

void fdt_ctx_walk_nodes(fdt_node_callback_t cb)
{
    if (!g_fdt_ctx.initialized || !cb) return;

    int offset = -1;
    int depth = 0;
    char path[256];

    while ((offset = fdt_next_node(g_fdt_ctx.fdt, offset, &depth)) >= 0) {
        int r = fdt_get_path(g_fdt_ctx.fdt, offset, path, sizeof(path));
        if (r < 0) path[0] = '\0';
        cb(path, offset, depth);
    }
}
